#include "crashhandler.h"
#include "pathmanager.h"
#include <QCoreApplication>
#include <QDir>
#include <QStandardPaths>
#include <QTextStream>
#include <QFile>
#include <iostream>

#ifdef Q_OS_UNIX
#include <signal.h>
#include <unistd.h>
#include <execinfo.h>
#include <cxxabi.h>
#include <dlfcn.h>
#include <sys/ucontext.h>
#endif

#ifdef Q_OS_WIN
#include <windows.h>
#include <dbghelp.h>
#include <psapi.h>
#pragma comment(lib, "dbghelp.lib")
#pragma comment(lib, "psapi.lib")
#endif

void CrashHandler::install()
{
#ifdef Q_OS_UNIX
    // 安装信号处理器处理各种崩溃信号
    signal(SIGSEGV, handleCrash);  // 段错误
    signal(SIGABRT, handleCrash);  // 异常终止
    signal(SIGFPE, handleCrash);   // 浮点异常
    signal(SIGILL, handleCrash);   // 非法指令
    signal(SIGBUS, handleCrash);   // 总线错误
#endif

#ifdef Q_OS_WIN
    // Windows下使用SetUnhandledExceptionFilter
    SetUnhandledExceptionFilter([](EXCEPTION_POINTERS* exceptionInfo) -> LONG {
        QString stackTrace = getStackTrace();
        writeCrashLog(stackTrace, exceptionInfo->ExceptionRecord->ExceptionCode);
        return EXCEPTION_EXECUTE_HANDLER;
    });
#endif

}

void CrashHandler::handleCrash(int sig)
{
#ifdef Q_OS_UNIX
    // 避免递归崩溃
    signal(sig, SIG_DFL);

    QString signalName;
    switch (sig) {
    case SIGSEGV: signalName = "SIGSEGV (段错误)"; break;
    case SIGABRT: signalName = "SIGABRT (异常终止)"; break;
    case SIGFPE:  signalName = "SIGFPE (浮点异常)"; break;
    case SIGILL:  signalName = "SIGILL (非法指令)"; break;
#ifdef SIGBUS
    case SIGBUS:  signalName = "SIGBUS (总线错误)"; break;
#endif
    default:      signalName = QString("信号 %1").arg(sig); break;
    }

    std::cout << "\n" << std::string(60, '=') << std::endl;
    std::cout << "程序崩溃！检测到信号: " << signalName.toStdString() << std::endl;
    std::cout << "时间: " << QDateTime::currentDateTime().toString().toStdString() << std::endl;
    std::cout << std::string(60, '=') << std::endl;

    // 获取并打印调用栈
    QString stackTrace = getStackTrace();
    std::cout << "调用栈:" << std::endl;
    std::cout << stackTrace.toStdString() << std::endl;
    std::cout << std::string(60, '=') << std::endl;

    // 写入崩溃日志文件
    writeCrashLog(stackTrace, sig);

    // 强制刷新输出
    std::cout.flush();
    std::cerr.flush();

    // 重新抛出信号以触发默认处理
    raise(sig);
#endif // Q_OS_UNIX
}

QString CrashHandler::getStackTrace()
{
    QString stackTrace;

#ifdef Q_OS_UNIX
    // Unix/Linux/macOS 使用 backtrace
    void *buffer[100];
    int nptrs = backtrace(buffer, 100);
    char **strings = backtrace_symbols(buffer, nptrs);

    if (strings == nullptr) {
        stackTrace = "无法获取调用栈信息\n";
        return stackTrace;
    }

    for (int i = 0; i < nptrs; i++) {
        QString frame = QString::fromLocal8Bit(strings[i]);

        // 尝试解析C++符号名
        QString demangledFrame = frame;

        // 在macOS上，符号格式通常是: <frame_number> <binary_name> <address> <mangled_name> + <offset>
        QStringList parts = frame.split(' ', Qt::SkipEmptyParts);
        if (parts.size() >= 4) {
            QString mangledName = parts[3];

            // 去除地址偏移部分
            int plusIndex = mangledName.indexOf('+');
            if (plusIndex != -1) {
                mangledName = mangledName.left(plusIndex).trimmed();
            }

            // 尝试解析C++符号
            if (!mangledName.isEmpty() && mangledName != "???") {
                int status;
                char* demangled = abi::__cxa_demangle(mangledName.toLocal8Bit().data(), 0, 0, &status);
                if (status == 0 && demangled) {
                    // 替换原始符号名
                    QString demangledStr = QString::fromLocal8Bit(demangled);
                    demangledFrame = QString("#%1  %2  %3  %4")
                                      .arg(parts[0])
                                      .arg(parts[1])
                                      .arg(parts[2])
                                      .arg(demangledStr);
                    free(demangled);
                }
            }
        }

        stackTrace += QString("#%1: %2\n").arg(i, 2, 10, QChar('0')).arg(demangledFrame);
    }

    free(strings);

#elif defined(Q_OS_WIN)
    // Windows 使用 CaptureStackBackTrace 和 SymFromAddr
    HANDLE process = GetCurrentProcess();
    SymInitialize(process, NULL, TRUE);

    void* stack[100];
    WORD frames = CaptureStackBackTrace(0, 100, stack, NULL);

    SYMBOL_INFO* symbol = (SYMBOL_INFO*)malloc(sizeof(SYMBOL_INFO) + 256);
    symbol->MaxNameLen = 255;
    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);

    for (WORD i = 0; i < frames; i++) {
        DWORD64 address = (DWORD64)(stack[i]);

        if (SymFromAddr(process, address, 0, symbol)) {
            stackTrace += QString("#%1: %2 [0x%3]\n")
                         .arg(i, 2, 10, QChar('0'))
                         .arg(QString::fromLocal8Bit(symbol->Name))
                         .arg(address, 0, 16);
        } else {
            stackTrace += QString("#%1: <unknown> [0x%2]\n")
                         .arg(i, 2, 10, QChar('0'))
                         .arg(address, 0, 16);
        }
    }

    free(symbol);
    SymCleanup(process);

#else
    stackTrace = "当前平台不支持调用栈获取\n";
#endif

    return stackTrace;
}

void CrashHandler::printStackTrace()
{
    QString stackTrace = getStackTrace();
    std::cout << "当前调用栈:" << std::endl;
    std::cout << stackTrace.toStdString() << std::endl;
}

void CrashHandler::writeCrashLog(const QString& stackTrace, int signal)
{
#ifdef Q_OS_UNIX
    // 使用PathManager获取标准崩溃日志目录
    QString logDir = PathManager::getCrashLogDirectory();
    if (logDir.isEmpty()) {
        // 备用方案
        logDir = PathManager::getLogDirectory();
    }

    QDir().mkpath(logDir);

    // 创建日志文件名
    QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss");
    QString logFileName = QString("%1/crash_%2.log").arg(logDir).arg(timestamp);

    QFile logFile(logFileName);
    if (logFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QTextStream out(&logFile);

        out << "=== LongImageReader 崩溃日志 ===" << Qt::endl;
        out << "时间: " << QDateTime::currentDateTime().toString() << Qt::endl;
        out << "信号: " << signal << Qt::endl;
        out << "进程ID: " << QCoreApplication::applicationPid() << Qt::endl;
        out << "应用程序路径: " << QCoreApplication::applicationFilePath() << Qt::endl;
        out << "工作目录: " << QDir::currentPath() << Qt::endl;
        out << Qt::endl;

        out << "调用栈:" << Qt::endl;
        out << stackTrace << Qt::endl;

        out << "=== 日志结束 ===" << Qt::endl;

        logFile.close();

        std::cout << "崩溃日志已保存到: " << logFileName.toStdString() << std::endl;
    } else {
        std::cout << "无法写入崩溃日志文件: " << logFileName.toStdString() << std::endl;
    }
#endif // Q_OS_UNIX
}

#ifdef Q_OS_WIN
void CrashHandler::writeCrashLog(const QString& stackTrace, DWORD exceptionCode)
{
    // 使用PathManager获取标准崩溃日志目录
    QString logDir = PathManager::getCrashLogDirectory();
    if (logDir.isEmpty()) {
        // 备用方案
        logDir = PathManager::getLogDirectory();
    }

    QDir().mkpath(logDir);

    // 创建日志文件名
    QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss");
    QString logFileName = QString("%1/crash_%2.log").arg(logDir).arg(timestamp);

    QFile logFile(logFileName);
    if (logFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QTextStream out(&logFile);

        out << "=== LongImageReader 崩溃日志 ===" << Qt::endl;
        out << "时间: " << QDateTime::currentDateTime().toString() << Qt::endl;
        out << "异常代码: 0x" << QString::number(exceptionCode, 16) << Qt::endl;
        out << "进程ID: " << QCoreApplication::applicationPid() << Qt::endl;
        out << "应用程序路径: " << QCoreApplication::applicationFilePath() << Qt::endl;
        out << "工作目录: " << QDir::currentPath() << Qt::endl;
        out << Qt::endl;

        out << "调用栈:" << Qt::endl;
        out << stackTrace << Qt::endl;

        out << "=== 日志结束 ===" << Qt::endl;

        logFile.close();

        std::cout << "崩溃日志已保存到: " << logFileName.toStdString() << std::endl;
    } else {
        std::cout << "无法写入崩溃日志文件: " << logFileName.toStdString() << std::endl;
    }
}
#endif // Q_OS_WIN